package com.mic.yzzn.plc.common.instructions.basic;

import java.io.Serializable;
import java.util.Date;

import com.mic.yzzn.plc.common.toolUtils.PLCNumberTransform;
import org.apache.commons.lang.*;


/**
 * Created by ASH on 16/3/7.
 */

abstract public class PLCInstruction implements Serializable
{
    /** 序列化版本号 */
	private static final long serialVersionUID = -7187533659287183576L;
	/** 指令的开头 */
    protected final String instructionHeader = "%";
    /** 高位站号 */
    protected String locationNumber_high;
    /** 低位站号 */
    protected String  locationNumber_low;
    /** 指令类型 */
    protected String instructionTypeCode;
    /** 较错码高位 */
    protected String BBC_high = "*";
    /** 较错码低位 */
    protected String BBC_low = "*";
    /** 指令结束符 */
    protected final char instructionEnd = '\r';

    /** 完整的指令字符串 */
    protected String instructionString;
    /** 指令序列号 */
    protected String serial;
    /** 指令发送/接收的时间 */
    protected Date timeStamp;


    /**
     * 头部get方法
     * @return 指令头
     */
    public String getInstructionHeader()
    {
        return instructionHeader;
    }

    /**
     * 高位站号get方法
     * @return 高位站号
     */
    public String getLocationNumber_high()
    {
        return locationNumber_high;
    }

    /**
     * 低位站号get方法
     * @return 低位站号
     */
    public String getLocationNumber_low()
    {
        return locationNumber_low;
    }

    /**
     * 指令类型码get方法
     * @return 指令类型码
     */
    public String getInstructionTypeCode()
    {
        return instructionTypeCode;
    }

    /**
     * 高位纠错码get方法
     * @return 高位纠错码
     */
    public String getBBC_high()
    {
        return BBC_high;
    }

    /**
     * 低位纠错码get方法
     * @return 低位纠错码
     */
    public String getBBC_low()
    {
        return BBC_low;
    }

    /**
     * 指令尾get方法
     * @return 指令尾
     */
    public char getInstructionEnd()
    {
        return instructionEnd;
    }
    
    /**
     * 指令字符串get方法
     * @return 指令字符串
     */
    public String getInstructionString()
	{
    	instructionString = toString();
		return instructionString;
	}

    public void setLocationNumber(String locationNumber) throws IllegalAccessException
    {
        if (locationNumber.length() != 2)
        {
            throw new IllegalArgumentException("locationNumber's length must be 2");
        }

        setLocationNumber_high(locationNumber.substring(0, 1));
        setLocationNumber_low(locationNumber.substring(1, 2));
    }

    /**
     * 高位站号set方法
     * @param locationNumber_high
     */
    public void setLocationNumber_high(String locationNumber_high)
    {
        this.locationNumber_high = locationNumber_high;
    }

    /**
     * 低位站号set方法
     * @param locationNumber_low
     */
    public void setLocationNumber_low(String locationNumber_low)
    {
        this.locationNumber_low = locationNumber_low;
    }

    /**
     * 指令类型set方法
     * @param instructionTypeCode
     */
    public void setInstructionTypeCode(String instructionTypeCode)
    {
        this.instructionTypeCode = instructionTypeCode;
    }

    /**
     * 忽略检错码
     */
    public void ignoreBBC()
    {
        this.BBC_high = "*";
        this.BBC_low = "*";
    }

    /**
     * 自动设置检错码
     */
    public void setBBC()
    {
        String hexString = PLCNumberTransform.HEXStringFromDECNumber(getXORByBit());
        int halfIndex = hexString.length() / 2 - 1;

        BBC_high = hexString.substring(0, halfIndex);
        BBC_low = hexString.substring(halfIndex, hexString.length());
    }

    /**
     * 计算自己每一位的异或结果并返回
     */
    protected int getXORByBit()
    {
        int i;
        int result = 0;

        // 指令头按位异或
        for (i = 0; i < instructionHeader.length(); ++i)
        {
            result ^= instructionHeader.charAt(i);
        }

        // 高位站号按位异或
        for (i = 0; i < locationNumber_high.length(); ++i)
        {
            result ^= locationNumber_high.charAt(i);
        }

        // 低位站号按位异或
        for (i = 0; i < locationNumber_low.length(); ++i)
        {
            result ^= locationNumber_low.charAt(i);
        }

        // 指令类型按位异或
        for (i = 0; i < instructionTypeCode.length(); ++i)
        {
            result ^= instructionTypeCode.charAt(i);
        }

        return result;
    }
    
    /**
     * 检测并返回较错码是否正确
     * @return 结果
     */
    public boolean checkBBC()
    {
    	boolean result = false;
    	
    	if (!BBC_high.equals("*") && !BBC_low.equals("*"))
		{
			String tempBBC = "";
			tempBBC = BBC_high + BBC_low;
			
			String correctBBC = PLCNumberTransform.HEXStringFromDECNumber(getXORByBit());
			
			result = tempBBC.equals(correctBBC);
		}
    	else
    	{
    		result = true;
    	}
    	
    	return result;
    }
    
    /**
     * 用字符串来初始化指令
     * @param instructionString
     */
    protected String initWithString(String instructionString) throws IllegalArgumentException
    {
    	// 去除尾部换行
    	StringUtils.chomp(instructionString);
    	if (instructionString.charAt(instructionString.length() - 1) == instructionEnd)
		{
			instructionString = instructionString.substring(0, instructionString.length() - 1);
		}
    	
    	if (!(instructionString.substring(0, 1)).equals(instructionHeader))
		{
			throw new IllegalArgumentException("PLC instruction must begin with \"%\"");
		}
    	
    	// 截断指令头
    	instructionString = StringUtils.substringAfter(instructionString, instructionHeader);

    	// 获取高位站号
    	setLocationNumber_high(StringUtils.substring(instructionString, 0, 1));
    	
    	// 获取低位站号
    	setLocationNumber_low(StringUtils.substring(instructionString, 1, 2));
    	
    	// 截断高低位站号
    	instructionString = StringUtils.substring(instructionString, 2, instructionString.length());

    	int length = instructionString.length(); 
    	
    	// 获取低位纠错码
    	BBC_low = StringUtils.substring(instructionString, length - 1, length);
    	
    	// 获取高位纠错码
    	BBC_high = StringUtils.substring(instructionString, length - 2, length - 1);
    	
    	// 截断高低位纠错码
    	instructionString = StringUtils.substring(instructionString, 0, length - 2);
    	
    	return instructionString;
    }
}
